#include <sdk.h>
#include <wx/tokenzr.h>
#include <wx/html/htmlwin.h>
#include <M_Code_StyledTextCtrl.h>
#include <editor_hooks.h>
#include <filegroupsandmasks.h>
#include <multiselectdlg.h>
#include "codecompletion.h"
#include "ccoptionsdlg.h"
#include "ccoptionsprjdlg.h"
#include "insertclassmethoddlg.h"
#include "selectincludefile.h"
#include "parser/ccdebuginfo.h"
#include "parser/cclogger.h"
#include "parser/parser.h"
#include "parser/tokenizer.h"
#include "doxygen_parser.h"
#include "gotofunctiondlg.h"

#define CC_CODECOMPLETION_DEBUG_OUTPUT 0


static wxString g_GlobalScope( _T( "<global>" ) );

namespace {
  PluginRegistrant<CodeCompletion> reg( _T( "CodeCompletion" ) );
}

namespace CodeCompletionHelper {
  inline bool LessFunctionScope( const CodeCompletion::FunctionScope& fs1, const CodeCompletion::FunctionScope& fs2 ) {
    int result = wxStricmp( fs1.Scope, fs2.Scope );
    if( result == 0 ) {
      result = wxStricmp( fs1.Name, fs2.Name );
      if( result == 0 ) {
        result = fs1.StartLine - fs2.StartLine;
      }
    }
    return result < 0;
  }

  inline bool EqualFunctionScope( const CodeCompletion::FunctionScope& fs1, const CodeCompletion::FunctionScope& fs2 ) {
    int result = wxStricmp( fs1.Scope, fs2.Scope );
    if( result == 0 ) {
      result = wxStricmp( fs1.Name, fs2.Name );
    }
    return result == 0;
  }

  inline bool LessNameSpace( const NameSpace& ns1, const NameSpace& ns2 ) {
    return ns1.Name < ns2.Name;
  }

  inline bool EqualNameSpace( const NameSpace& ns1, const NameSpace& ns2 ) {
    return ns1.Name == ns2.Name;
  }

  inline wxChar GetLastNonWhitespaceChar( M_Code_StyledTextCtrl* control, int position ) {
    if( !control ) {
      return 0;
    }
    while( --position > 0 ) {
      const int style = control->GetStyleAt( position );
      if( control->IsComment( style ) ) {
        continue;
      }
      const wxChar ch = control->GetCharAt( position );
      if( ch <= _T( ' ' ) ) {
        continue;
      }
      return ch;
    }
    return 0;
  }

  inline wxChar GetNextNonWhitespaceChar( M_Code_StyledTextCtrl* control, int position ) {
    if( !control ) {
      return 0;
    }
    const int totalLength = control->GetLength();
    --position;
    while( ++position < totalLength ) {
      const int style = control->GetStyleAt( position );
      if( control->IsComment( style ) ) {
        continue;
      }
      const wxChar ch = control->GetCharAt( position );
      if( ch <= _T( ' ' ) ) {
        continue;
      }
      return ch;
    }
    return 0;
  }

  inline int CompareStringLen( const wxString& first, const wxString& second ) {
    return second.Len() - first.Len();
  }

  inline bool TestIncludeLine( wxString const &line ) {
    size_t index = line.find( _T( '#' ) );
    if( index == wxString::npos ) {
      return false;
    }
    ++index;
    for( ; index < line.length(); ++index ) {
      if( line[index] != _T( ' ' ) && line[index] != _T( '\t' ) ) {
        if( line.Mid( index, 7 ) == _T( "include" ) ) {
          return true;
        }
        break;
      }
    }
    return false;
  }

  inline bool EditorHasNameUnderCursor( wxString& NameUnderCursor, bool& IsInclude ) {
    bool ReturnValue = false;
    if( M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() ) {
      M_Code_StyledTextCtrl* control = ed->GetControl();
      const int pos = control->GetCurrentPos();
      const wxString line = control->GetLine( control->LineFromPosition( pos ) );
      const wxRegEx reg( _T( "^[ \t]*#[ \t]*include[ \t]+[\"<]([^\">]+)[\">]" ) );
      wxString inc;
      if( reg.Matches( line ) ) {
        inc = reg.GetMatch( line, 1 );
      }
      if( !inc.IsEmpty() ) {
        NameUnderCursor = inc;
        ReturnValue = true;
        IsInclude = true;
      } else {
        const int start = control->WordStartPosition( pos, true );
        const int end = control->WordEndPosition( pos, true );
        const wxString word = control->GetTextRange( start, end );
        if( !word.IsEmpty() ) {
          NameUnderCursor.Clear();
          NameUnderCursor << word;
          ReturnValue = true;
          IsInclude = false;
        }
      }
    }
    return ReturnValue;
  }

  struct GotoDeclarationItem {
    wxString filename;
    unsigned line;
  };

  static wxString AutocompGetName( const wxString& selected ) {
    size_t nameEnd = selected.find_first_of( _T( "(: " ) );
    return selected.substr( 0, nameEnd );
  }

}

static const char * cpp_keyword_xpm[] = {
  "16 16 2 1",
  "     c None",
  ".    c #04049B",
  "                ",
  "  .......       ",
  " .........      ",
  " ..     ..      ",
  "..              ",
  "..   ..     ..  ",
  "..   ..     ..  ",
  ".. ...... ......",
  ".. ...... ......",
  "..   ..     ..  ",
  "..   ..     ..  ",
  "..      ..      ",
  "...     ..      ",
  " .........      ",
  "  .......       ",
  "                "
};

static const char * unknown_keyword_xpm[] = {
  "16 16 7 1",
  "     c None",
  ".    c #FF8800",
  "+    c #FF8D0B",
  "@    c #FF9115",
  "#    c #FFA948",
  "$    c #FFC686",
  "%    c #FFFFFF",
  "                ",
  "                ",
  "      ....      ",
  "    ........    ",
  "   ..+@+.....   ",
  "   .+#$#+....   ",
  "  ..@$%$@.....  ",
  "  ..+#$#+.....  ",
  "  ...+@+......  ",
  "  ............  ",
  "   ..........   ",
  "   ..........   ",
  "    ........    ",
  "      ....      ",
  "                ",
  "                "
};

static const char* header_file_xpm[] = {
  "16 16 9 1",
  "   c None",
  "+  c #D23E39",
  "$  c #CF0C0A",
  "@  c #CB524B",
  "&  c #E2D8D8",
  "#  c #C7C7C4",
  "_  c #E4B9B5",
  "-  c #F7F9F7",
  "=  c #EBE9E7",
  "  #########     ",
  "  #=-----####   ",
  "  #--------=##  ",
  "  #--------=-#  ",
  "  #--=@_-----#  ",
  "  #--=+_-----#  ",
  "  #--=++@_---#  ",
  "  #--&$@@$=--#  ",
  "  #--&$__$&=-#  ",
  "  #--&$__$&=-#  ",
  "  #--&$__$&=-#  ",
  "  #-==#=&#==-#  ",
  "  #-========-#  ",
  "  #----=====-#  ",
  "  ############  ",
  "                "
};

int idMenuGotoFunction = XRCID( "idMenuGotoFunction" );
int idMenuGotoPrevFunction = XRCID( "idMenuGotoPrevFunction" );
int idMenuGotoNextFunction = XRCID( "idMenuGotoNextFunction" );
int idMenuGotoDeclaration = XRCID( "idMenuGotoDeclaration" );
int idMenuGotoImplementation = XRCID( "idMenuGotoImplementation" );
int idMenuOpenIncludeFile = XRCID( "idMenuOpenIncludeFile" );
int idMenuFindReferences = XRCID( "idMenuFindReferences" );
int idViewRenameSymbols = XRCID( "idViewRenameSymbols" );
int idViewClassBrowser = XRCID( "idViewClassBrowser" );
int idCurrentProjectReparse = XRCID( "idCurrentProjectReparse" );
int idSelectedProjectReparse = wxNewId();
int idSelectedFileReparse = wxNewId();
int idEditorSubMenu = wxNewId();
int idClassMethod = wxNewId();
int idUnimplementedClassMethods = wxNewId();
int idGotoDeclaration = wxNewId();
int idGotoImplementation = wxNewId();
int idOpenIncludeFile = wxNewId();
int idRealtimeParsingTimer = wxNewId();
int idToolbarTimer = wxNewId();
int idProjectSavedTimer = wxNewId();
int idReparsingTimer = wxNewId();
int idEditorActivatedTimer = wxNewId();


#define REALTIME_PARSING_DELAY    500
#define TOOLBAR_REFRESH_DELAY     150
#define EDITOR_ACTIVATED_DELAY    300

BEGIN_EVENT_TABLE( CodeCompletion, cbCodeCompletionPlugin )
  EVT_UPDATE_UI_RANGE( idMenuGotoFunction, idCurrentProjectReparse, CodeCompletion::OnUpdateUI )
  EVT_MENU( idMenuGotoFunction, CodeCompletion::OnGotoFunction )
  EVT_MENU( idMenuGotoPrevFunction, CodeCompletion::OnGotoPrevFunction )
  EVT_MENU( idMenuGotoNextFunction, CodeCompletion::OnGotoNextFunction )
  EVT_MENU( idMenuGotoDeclaration, CodeCompletion::OnGotoDeclaration )
  EVT_MENU( idMenuGotoImplementation, CodeCompletion::OnGotoDeclaration )
  EVT_MENU( idMenuFindReferences, CodeCompletion::OnFindReferences )
  EVT_MENU( idViewRenameSymbols, CodeCompletion::OnRenameSymbols )
  EVT_MENU( idClassMethod, CodeCompletion::OnClassMethod )
  EVT_MENU( idUnimplementedClassMethods, CodeCompletion::OnUnimplementedClassMethods )
  EVT_MENU( idGotoDeclaration, CodeCompletion::OnGotoDeclaration )
  EVT_MENU( idGotoImplementation, CodeCompletion::OnGotoDeclaration )
  EVT_MENU( idOpenIncludeFile, CodeCompletion::OnOpenIncludeFile )
  EVT_MENU( idMenuOpenIncludeFile, CodeCompletion::OnOpenIncludeFile )
  EVT_MENU( idViewClassBrowser, CodeCompletion::OnViewClassBrowser )
  EVT_MENU( idCurrentProjectReparse, CodeCompletion::OnCurrentProjectReparse )
  EVT_MENU( idSelectedProjectReparse, CodeCompletion::OnSelectedProjectReparse )
  EVT_MENU( idSelectedFileReparse, CodeCompletion::OnSelectedFileReparse )
  EVT_CHOICE( XRCID( "chcCodeCompletionScope" ), CodeCompletion::OnScope )
  EVT_CHOICE( XRCID( "chcCodeCompletionFunction" ), CodeCompletion::OnFunction )
END_EVENT_TABLE()

CodeCompletion::CodeCompletion() :
  m_InitDone( false ),
  m_CodeRefactoring( m_NativeParser ),
  m_EditorHookId( 0 ),
  m_TimerRealtimeParsing( this, idRealtimeParsingTimer ),
  m_TimerToolbar( this, idToolbarTimer ),
  m_TimerProjectSaved( this, idProjectSavedTimer ),
  m_TimerReparsing( this, idReparsingTimer ),
  m_TimerEditorActivated( this, idEditorActivatedTimer ),
  m_LastEditor( 0 ),
  m_ToolBar( 0 ),
  m_Function( 0 ),
  m_Scope( 0 ),
  m_ToolbarNeedRefresh( true ),
  m_ToolbarNeedReparse( false ),
  m_CurrentLine( 0 ),
  m_NeedReparse( false ),
  m_CurrentLength( -1 ),
  m_NeedsBatchColour( true ),
  m_CCMaxMatches( 16384 ),
  m_CCAutoAddParentheses( true ),
  m_CCDetectImplementation( false ),
  m_CCEnableHeaders( false ),
  m_CCEnablePlatformCheck( true ),
  m_SystemHeadersThreadCS(),
  m_DocHelper( this ) {
  CCLogger::Get()->Init( this, g_idCCLogger, g_idCCDebugLogger );
  if( !Manager::LoadResource( _T( "CodeCompletion.zip" ) ) ) {
    NotifyMissingFile( _T( "CodeCompletion.zip" ) );
  }
  Connect( g_idCCLogger, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnCCLogger ) );
  Connect( g_idCCDebugLogger, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnCCDebugLogger ) );
  Connect( ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CodeCompletion::OnParserStart ) );
  Connect( ParserCommon::idParserEnd, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CodeCompletion::OnParserEnd ) );
  Connect( idRealtimeParsingTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnRealtimeParsingTimer ) );
  Connect( idToolbarTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnToolbarTimer ) );
  Connect( idProjectSavedTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnProjectSavedTimer ) );
  Connect( idReparsingTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnReparsingTimer ) );
  Connect( idEditorActivatedTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnEditorActivatedTimer ) );
  Connect( idSystemHeadersThreadUpdate, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnSystemHeadersThreadUpdate ) );
  Connect( idSystemHeadersThreadFinish, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnSystemHeadersThreadFinish ) );
  Connect( idSystemHeadersThreadError, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnSystemHeadersThreadError ) );
}

CodeCompletion::~CodeCompletion() {
  Disconnect( g_idCCLogger, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnCCLogger ) );
  Disconnect( g_idCCDebugLogger, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnCCDebugLogger ) );
  Disconnect( ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CodeCompletion::OnParserStart ) );
  Disconnect( ParserCommon::idParserEnd, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CodeCompletion::OnParserEnd ) );
  Disconnect( idRealtimeParsingTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnRealtimeParsingTimer ) );
  Disconnect( idToolbarTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnToolbarTimer ) );
  Disconnect( idProjectSavedTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnProjectSavedTimer ) );
  Disconnect( idReparsingTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnReparsingTimer ) );
  Disconnect( idEditorActivatedTimer, wxEVT_TIMER, wxTimerEventHandler( CodeCompletion::OnEditorActivatedTimer ) );
  Disconnect( idSystemHeadersThreadUpdate, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnSystemHeadersThreadUpdate ) );
  Disconnect( idSystemHeadersThreadFinish, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnSystemHeadersThreadFinish ) );
  Disconnect( idSystemHeadersThreadError, wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler( CodeCompletion::OnSystemHeadersThreadError ) );
  while( !m_SystemHeadersThreads.empty() ) {
    SystemHeadersThread* thread = m_SystemHeadersThreads.front();
    if( thread->IsAlive() && thread->IsRunning() ) {
      thread->Wait();
    }
    m_SystemHeadersThreads.pop_front();
  }
}

void CodeCompletion::OnAttach() {
  m_EditMenu = 0;
  m_SearchMenu = 0;
  m_ViewMenu = 0;
  m_ProjectMenu = 0;
  m_ToolBar = 0;
  m_Function = 0;
  m_Scope = 0;
  m_FunctionsScope.clear();
  m_NameSpaces.clear();
  m_AllFunctionsScopes.clear();
  m_ToolbarNeedRefresh = true;
  m_LastFile.clear();
  RereadOptions();
  m_NativeParser.SetNextHandler( this );
  m_NativeParser.CreateClassBrowser();
  EditorHooks::HookFunctorBase* myhook = new EditorHooks::HookFunctor<CodeCompletion>( this, &CodeCompletion::EditorEventHook );
  m_EditorHookId = EditorHooks::RegisterHook( myhook );
  Manager* pm = Manager::Get();
  pm->RegisterEventSink( cbEVT_APP_STARTUP_DONE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnAppDoneStartup ) );
  pm->RegisterEventSink( cbEVT_WORKSPACE_CHANGED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnWorkspaceChanged ) );
  pm->RegisterEventSink( cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnProjectActivated ) );
  pm->RegisterEventSink( cbEVT_PROJECT_CLOSE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnProjectClosed ) );
  pm->RegisterEventSink( cbEVT_PROJECT_SAVE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnProjectSaved ) );
  pm->RegisterEventSink( cbEVT_PROJECT_FILE_ADDED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnProjectFileAdded ) );
  pm->RegisterEventSink( cbEVT_PROJECT_FILE_REMOVED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnProjectFileRemoved ) );
  pm->RegisterEventSink( cbEVT_PROJECT_FILE_CHANGED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnProjectFileChanged ) );
  pm->RegisterEventSink( cbEVT_EDITOR_SAVE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnEditorSave ) );
  pm->RegisterEventSink( cbEVT_EDITOR_OPEN, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnEditorOpen ) );
  pm->RegisterEventSink( cbEVT_EDITOR_ACTIVATED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnEditorActivated ) );
  pm->RegisterEventSink( cbEVT_EDITOR_CLOSE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>( this, &CodeCompletion::OnEditorClosed ) );
  m_DocHelper.OnAttach();
}

void CodeCompletion::OnRelease( bool appShutDown ) {
  m_NativeParser.RemoveClassBrowser( appShutDown );
  m_NativeParser.ClearParsers();
  m_NativeParser.SetNextHandler( nullptr );
  EditorHooks::UnregisterHook( m_EditorHookId, true );
  Manager::Get()->RemoveAllEventSinksFor( this );
  m_FunctionsScope.clear();
  m_NameSpaces.clear();
  m_AllFunctionsScopes.clear();
  m_ToolbarNeedRefresh = false;
  if( m_EditMenu ) {
    m_EditMenu->Delete( idViewRenameSymbols );
  }
  if( m_SearchMenu ) {
    m_SearchMenu->Delete( idMenuGotoFunction );
    m_SearchMenu->Delete( idMenuGotoPrevFunction );
    m_SearchMenu->Delete( idMenuGotoNextFunction );
    m_SearchMenu->Delete( idMenuGotoDeclaration );
    m_SearchMenu->Delete( idMenuGotoImplementation );
    m_SearchMenu->Delete( idMenuFindReferences );
    m_SearchMenu->Delete( idMenuOpenIncludeFile );
  }
  m_DocHelper.OnRelease();
}

cbConfigurationPanel* CodeCompletion::GetConfigurationPanel( wxWindow* parent ) {
  return new CCOptionsDlg( parent, &m_NativeParser, this, &m_DocHelper );
}

cbConfigurationPanel* CodeCompletion::GetProjectConfigurationPanel( wxWindow* parent, M_Code_Project* project ) {
  return new CCOptionsProjectDlg( parent, project, &m_NativeParser );
}

void CodeCompletion::BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data ) {
  if( !menu || !IsAttached() || !m_InitDone ) {
    return;
  }
  if( type == mtEditorManager ) {
    if( M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() ) {
      if( !IsProviderFor( ed ) ) {
        return;
      }
    }
    wxString NameUnderCursor, msg, tmp1;
    bool IsInclude = false;
    const bool nameUnderCursor = CodeCompletionHelper::EditorHasNameUnderCursor( NameUnderCursor, IsInclude );
    tmp1 = NameUnderCursor;
    if( nameUnderCursor ) {
      if( IsInclude ) {
        if( NameUnderCursor.Len() > 12 ) {
          tmp1.Remove( 0, tmp1.length() - 5 );
          msg = F( _T( "%4.4s...%s" ), NameUnderCursor.wx_str(), tmp1.wx_str() );
        } else {
          msg << NameUnderCursor.wx_str();
        }
        menu->Insert( 0, idOpenIncludeFile, F( _T( "打开: ' %s ' " ), msg.wx_str() ) );
        menu->Insert( 1, wxID_SEPARATOR, wxEmptyString );
      } else {
        if( NameUnderCursor.Len() > 10 ) {
          tmp1.Remove( 0, tmp1.length() - 3 );
          msg = F( _T( "%4.4s...%s" ), NameUnderCursor.wx_str(), tmp1.wx_str() );
        } else {
          msg << NameUnderCursor.wx_str();
        }
        size_t pos = 0;
        menu->Insert( pos, idGotoDeclaration, F( _T( "查找声明: ' %s ' " ), msg.wx_str() ) );
        ++pos;
        menu->Insert( pos, idGotoImplementation, F( _T( "查找方法: ' %s ' " ), msg.wx_str() ) );
        ++pos;
        if( m_NativeParser.GetParser().Done() ) {
          menu->Insert( pos, idMenuFindReferences, F( _T( "查找引用: ' %s ' " ), msg.wx_str() ) );
          ++pos;
        }
      }
    }
    const int insertId = menu->FindItem( _T( "插入" ) );
    if( insertId != wxNOT_FOUND ) {
      if( wxMenuItem* insertMenu = menu->FindItem( insertId, 0 ) ) {
        if( wxMenu* subMenu = insertMenu->GetSubMenu() ) {
          subMenu->Append( idClassMethod, _T( "类方法 声明/实现..." ) );
          subMenu->Append( idUnimplementedClassMethods, _T( "没有实现的所有类方法..." ) );
        }
      }
    }
    if( m_NativeParser.GetParser().Done() && nameUnderCursor && !IsInclude ) {
      menu->Append( idViewRenameSymbols, _T( "重命名符号" ) );
    }
  } else if( type == mtProjectManager ) {
    if( data ) {
      if( data->GetKind() == FileTreeData::ftdkProject ) {
        size_t position = menu->GetMenuItemCount();
        int id = menu->FindItem( _T( "Build" ) );
        if( id != wxNOT_FOUND ) {
          menu->FindChildItem( id, &position );
        }
        menu->Insert( position, idSelectedProjectReparse, _T( "重新分析此项目" ) );
      } else if( data->GetKind() == FileTreeData::ftdkFile ) {
        menu->Append( idSelectedFileReparse, _T( "重新分析此文件" ) );
      }
    }
  }
}

bool CodeCompletion::BuildToolBar( wxToolBar* toolBar ) {
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/CodeCompletion_ToolBar.xrc" ) ;
  xml_res->Load( resPath );
  Manager::Get()->AddonToolBar( toolBar, _T( "codecompletion_toolbar" ) );
  m_Function = XRCCTRL( *toolBar, "chcCodeCompletionFunction", wxChoice );
  m_Scope = XRCCTRL( *toolBar, "chcCodeCompletionScope", wxChoice );
  m_ToolBar = toolBar;
  UpdateToolBar();
  EnableToolbarTools( false );
  return true;
}

CodeCompletion::CCProviderStatus CodeCompletion::GetProviderStatusFor( M_Code_Editor* ed ) {
  EditorColourSet *colour_set = ed->GetColourSet();
  if( colour_set && ed->GetLanguage() == colour_set->GetHighlightLanguage( _T( "C/C++" ) ) ) {
    return ccpsActive;
  }
  switch( ParserCommon::FileType( ed->GetFilename() ) ) {
    case ParserCommon::ftHeader:
    case ParserCommon::ftSource:
      return ccpsActive;
    case ParserCommon::ftOther:
    default:
      break;
  }
  return ccpsUniversal;
}

std::vector<CodeCompletion::CCToken> CodeCompletion::GetAutocompList( bool isAuto, M_Code_Editor* ed, int& tknStart, int& tknEnd ) {
  std::vector<CCToken> tokens;
  if( !IsAttached() || !m_InitDone ) {
    return tokens;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  const int style = stc->GetStyleAt( tknEnd );
  const wxChar curChar = stc->GetCharAt( tknEnd - 1 );
  if( isAuto ) {
    if( ( curChar == _T( ':' ) && stc->GetCharAt( tknEnd - 2 ) != _T( ':' ) ) || ( curChar == _T( '>' ) && stc->GetCharAt( tknEnd - 2 ) != _T( '-' ) )
        || ( wxString( _T( "<\"/" ) ).Find( curChar ) != wxNOT_FOUND && !stc->IsPreprocessor( style ) ) ) {
      return tokens;
    }
  }
  const int lineIndentPos = stc->GetLineIndentPosition( stc->GetCurrentLine() );
  const wxChar lineFirstChar = stc->GetCharAt( lineIndentPos );
  if( lineFirstChar == _T( '#' ) ) {
    const int startPos = stc->WordStartPosition( lineIndentPos + 1, true );
    const int endPos = stc->WordEndPosition( lineIndentPos + 1, true );
    const wxString str = stc->GetTextRange( startPos, endPos );
    if( str == _T( "include" ) && tknEnd > endPos ) {
      DoCodeCompleteIncludes( ed, tknStart, tknEnd, tokens );
    } else if( endPos >= tknEnd && tknEnd > lineIndentPos ) {
      DoCodeCompletePreprocessor( tknStart, tknEnd, ed, tokens );
    } else if( ( str == _T( "define" ) || str == _T( "if" ) || str == _T( "ifdef" ) || str == _T( "ifndef" )
                 || str == _T( "elif" ) || str == _T( "elifdef" ) || str == _T( "elifndef" ) || str == _T( "undef" ) ) && tknEnd > endPos ) {
      DoCodeComplete( tknEnd, ed, tokens, true );
    }
    return tokens;
  } else if( curChar == _T( '#' ) ) {
    return tokens;
  } else if( lineFirstChar == _T( ':' ) && curChar == _T( ':' ) ) {
    return tokens;
  }
  if( stc->IsString( style ) || stc->IsComment( style ) || stc->IsCharacter( style ) || stc->IsPreprocessor( style ) ) {
    return tokens;
  }
  DoCodeComplete( tknEnd, ed, tokens );
  return tokens;
}

void CodeCompletion::DoCodeComplete( int caretPos, M_Code_Editor* ed, std::vector<CCToken>& tokens, bool preprocessorOnly ) {
  const bool caseSens = m_NativeParser.GetParser().Options().caseSensitive;
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  TokenIdxSet result;
  if( m_NativeParser.MarkItemsByAI( result, m_NativeParser.GetParser().Options().useSmartSense, true, caseSens, caretPos )
      || m_NativeParser.LastAISearchWasGlobal() ) {
    if( result.size() <= m_CCMaxMatches ) {
      wxImageList* ilist = m_NativeParser.GetImageList();
      stc->ClearRegisteredImages();
      tokens.reserve( result.size() );
      std::set<int> alreadyRegistered;
      StringSet uniqueStrings;
      TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
      CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
      for( TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it ) {
        const Token* token = tree->at( *it );
        if( !token || token->m_Name.IsEmpty() ) {
          continue;
        }
        if( preprocessorOnly && token->m_TokenKind != tkMacroDef ) {
          continue;
        }
        int iidx = m_NativeParser.GetTokenKindImage( token );
        if( alreadyRegistered.find( iidx ) == alreadyRegistered.end() ) {
          stc->RegisterImage( iidx, ilist->GetBitmap( iidx ) );
          alreadyRegistered.insert( iidx );
        }
        wxString dispStr;
        if( token->m_TokenKind & tkAnyFunction ) {
          if( m_DocHelper.IsEnabled() ) {
            dispStr = _T( "(): " ) + token->m_FullType;
          } else {
            dispStr = token->GetFormattedArgs() << _T( ": " ) << token->m_FullType;
          }
        } else if( token->m_TokenKind == tkVariable ) {
          dispStr = _T( ": " ) + token->m_FullType;
        }
        tokens.push_back( CCToken( token->m_Index, token->m_Name + dispStr, token->m_Name, token->m_IsTemp ? 0 : 5, iidx ) );
        uniqueStrings.insert( token->m_Name );
        if( token->m_TokenKind == tkNamespace && token->m_Aliases.size() ) {
          for( size_t i = 0; i < token->m_Aliases.size(); ++i ) {
            tokens.push_back( CCToken( token->m_Index, token->m_Aliases[i] + dispStr, token->m_Aliases[i], 5, iidx ) );
            uniqueStrings.insert( token->m_Aliases[i] );
          }
        }
      }
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
      if( m_NativeParser.LastAISearchWasGlobal() && !preprocessorOnly ) {
        EditorColourSet* colour_set = ed->GetColourSet();
        if( colour_set ) { //关键字
          wxString lastSearch = m_NativeParser.LastAIGlobalSearch().Lower();
          int iidx = ilist->GetImageCount();
          FileType fTp = FileTypeOf( ed->GetShortName() );
          bool isC = ( fTp == ftHeader || fTp == ftSource || fTp == ftTemplateSource );
          HighlightLanguage lang = ed->GetLanguage();
          if( lang == HL_NONE ) {
            lang = colour_set->GetLanguageForFilename( ed->GetFilename() );
          }
          wxString strLang = colour_set->GetLanguageName( lang );
          if( isC && strLang == _T( "C/C++" ) ) {
            stc->RegisterImage( iidx, wxBitmap( cpp_keyword_xpm ) );
          } else {
            stc->RegisterImage( iidx, wxBitmap( unknown_keyword_xpm ) );
          }
          for( int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i ) {
            if( !m_LexerKeywordsToInclude[i] )
            { continue; }
            wxString keywords = colour_set->GetKeywords( lang, i );
            wxStringTokenizer tkz( keywords, _T( " \t\r\n" ), wxTOKEN_STRTOK );
            while( tkz.HasMoreTokens() ) {
              wxString kw = tkz.GetNextToken();
              if( kw.Lower().StartsWith( lastSearch ) && uniqueStrings.find( kw ) == uniqueStrings.end() ) {
                tokens.push_back( CCToken( wxNOT_FOUND, kw, iidx ) );
              }
            }
          }
        }
      }
    } else if( !stc->CallTipActive() ) {
      wxString msg = _T( "内容过长与被抑制." );
      stc->CallTipShow( stc->GetCurrentPos(), msg );
    }
  } else if( !stc->CallTipActive() ) {
    if( !m_NativeParser.GetParser().Done() ) {
      wxString msg = _T( "解析器仍然在解析文件." );
      stc->CallTipShow( stc->GetCurrentPos(), msg );
      msg += m_NativeParser.GetParser().NotDoneReason();
      CCLogger::Get()->DebugLog( msg );
    }
  }
}

void CodeCompletion::DoCodeCompletePreprocessor( int tknStart, int tknEnd, M_Code_Editor* ed, std::vector<CCToken>& tokens ) {
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  if( stc->GetLexer() != wxSCI_LEX_CPP ) {
    const FileType fTp = FileTypeOf( ed->GetShortName() );
    if( fTp != ftSource && fTp != ftHeader && fTp != ftTemplateSource && fTp != ftResource ) {
      return;
    }
  }
  const wxString text = stc->GetTextRange( tknStart, tknEnd );
  wxStringVec macros;
  macros.push_back( _T( "define" ) );
  macros.push_back( _T( "elif" ) );
  macros.push_back( _T( "elifdef" ) );
  macros.push_back( _T( "elifndef" ) );
  macros.push_back( _T( "else" ) );
  macros.push_back( _T( "endif" ) );
  macros.push_back( _T( "error" ) );
  macros.push_back( _T( "if" ) );
  macros.push_back( _T( "ifdef" ) );
  macros.push_back( _T( "ifndef" ) );
  macros.push_back( _T( "include" ) );
  macros.push_back( _T( "line" ) );
  macros.push_back( _T( "pragma" ) );
  macros.push_back( _T( "undef" ) );
  const wxString idxStr = F( _T( "\n%d" ), PARSER_IMG_MACRO_DEF );
  for( size_t i = 0; i < macros.size(); ++i ) {
    if( text.IsEmpty() || macros[i][0] == text[0] ) {
      tokens.push_back( CCToken( wxNOT_FOUND, macros[i], PARSER_IMG_MACRO_DEF ) );
    }
  }
  stc->ClearRegisteredImages();
  stc->RegisterImage( PARSER_IMG_MACRO_DEF,
                      m_NativeParser.GetImageList()->GetBitmap( PARSER_IMG_MACRO_DEF ) );
}

void CodeCompletion::DoCodeCompleteIncludes( M_Code_Editor* ed, int& tknStart, int tknEnd, std::vector<CCToken>& tokens ) {
  if( !m_CCEnableHeaders ) {
    return;
  }
  const wxString curFile( ed->GetFilename() );
  const wxString curPath( wxFileName( curFile ).GetPath() );
  FileType ft = FileTypeOf( ed->GetShortName() );
  if( ft != ftHeader && ft != ftSource && ft != ftTemplateSource ) {
    return;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  const int lineStartPos = stc->PositionFromLine( stc->GetCurrentLine() );
  wxString line = stc->GetCurLine();
  line.Trim();
  if( line.IsEmpty() || !CodeCompletionHelper::TestIncludeLine( line ) ) {
    return;
  }
  int keyPos = line.Find( _T( '"' ) );
  if( keyPos == wxNOT_FOUND || keyPos >= tknEnd - lineStartPos ) {
    keyPos = line.Find( _T( '<' ) );
  }
  if( keyPos == wxNOT_FOUND || keyPos >= tknEnd - lineStartPos ) {
    return;
  }
  ++keyPos;
  wxString filename = line.SubString( keyPos, tknEnd - lineStartPos - 1 );
  filename.Replace( _T( "\\" ), _T( "/" ), true );
  if( !filename.empty() && ( filename.Last() == _T( '"' ) || filename.Last() == _T( '>' ) ) ) {
    filename.RemoveLast();
  }
  size_t maxFiles = m_CCMaxMatches;
  if( filename.IsEmpty() && maxFiles > 3000 ) {
    maxFiles = 3000;
  }
  StringSet files;
  M_Code_Project* project = m_NativeParser.GetProjectByEditor( ed );
  wxCriticalSectionLocker locker( m_SystemHeadersThreadCS );
  wxArrayString& incDirs = GetSystemIncludeDirs( project, project ? project->GetModified() : true );
  for( size_t i = 0; i < incDirs.GetCount(); ++i ) {
    SystemHeadersMap::const_iterator shm_it = m_SystemHeadersMap.find( incDirs[i] );
    if( shm_it != m_SystemHeadersMap.end() ) {
      const StringSet& headers = shm_it->second;
      for( StringSet::const_iterator ss_it = headers.begin(); ss_it != headers.end(); ++ss_it ) {
        const wxString& file = *ss_it;
        if( file.StartsWith( filename ) ) {
          files.insert( file );
          if( files.size() > maxFiles ) {
            break;
          }
        }
      }
      if( files.size() > maxFiles ) {
        break;
      }
    }
  }
  if( project ) {
    wxArrayString buildTargets;
    ProjectFile* pf = project ? project->GetFileByFilename( curFile, false ) : 0;
    if( pf ) {
      buildTargets = pf->buildTargets;
    }
    const wxArrayString localIncludeDirs = GetLocalIncludeDirs( project, buildTargets );
    for( FilesList::const_iterator it = project->GetFilesList().begin();
         it != project->GetFilesList().end(); ++it ) {
      pf = *it;
      if( pf && FileTypeOf( pf->relativeFilename ) == ftHeader ) {
        wxString file = pf->file.GetFullPath();
        wxString header;
        for( size_t j = 0; j < localIncludeDirs.GetCount(); ++j ) {
          const wxString& dir = localIncludeDirs[j];
          if( file.StartsWith( dir ) ) {
            header = file.Mid( dir.Len() );
            header.Replace( _T( "\\" ), _T( "/" ) );
            break;
          }
        }
        if( header.IsEmpty() ) {
          if( pf->buildTargets != buildTargets ) {
            continue;
          }
          wxFileName fn( file );
          fn.MakeRelativeTo( curPath );
          header = fn.GetFullPath( wxPATH_UNIX );
        }
        if( header.StartsWith( filename ) ) {
          files.insert( header );
          if( files.size() > maxFiles ) {
            break;
          }
        }
      }
    }
  }
  if( !files.empty() ) {
    tknStart = lineStartPos + keyPos;
    tokens.reserve( files.size() );
    for( StringSet::const_iterator ssIt = files.begin(); ssIt != files.end(); ++ssIt ) {
      tokens.push_back( CCToken( wxNOT_FOUND, *ssIt, 0 ) );
    }
    stc->ClearRegisteredImages();
    stc->RegisterImage( 0, wxBitmap( header_file_xpm ) );
  }
}

std::vector<CodeCompletion::CCCallTip> CodeCompletion::GetCallTips( int pos, int style, M_Code_Editor* ed, int& argsPos ) {
  std::vector<CCCallTip> tips;
  if( !IsAttached() || !m_InitDone || style == wxSCI_C_WXSMITH || !m_NativeParser.GetParser().Done() ) {
    return tips;
  }
  int typedCommas = 0;
  wxArrayString items;
  argsPos = m_NativeParser.GetCallTips( items, typedCommas, ed, pos );
  StringSet uniqueTips;
  for( size_t i = 0; i < items.GetCount(); ++i ) {
    if( uniqueTips.find( items[i] ) == uniqueTips.end() && !items[i].IsEmpty()
        && typedCommas <= m_NativeParser.CountCommas( items[i], 0 ) ) {
      uniqueTips.insert( items[i] );
      int hlStart = wxSCI_INVALID_POSITION;
      int hlEnd   = wxSCI_INVALID_POSITION;
      m_NativeParser.GetCallTipHighlight( items[i], &hlStart, &hlEnd, typedCommas );
      tips.push_back( CCCallTip( items[i], hlStart, hlEnd ) );
    }
  }
  return tips;
}

wxString CodeCompletion::GetDocumentation( const CCToken& token ) {
  return m_DocHelper.GenerateHTML( token.id, m_NativeParser.GetParser().GetTokenTree() );
}

std::vector<CodeCompletion::CCToken> CodeCompletion::GetTokenAt( int pos, M_Code_Editor* ed, bool& ( allowCallTip ) ) {
  std::vector<CCToken> tokens;
  if( !IsAttached() || !m_InitDone ) {
    return tokens;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  const int style = stc->GetStyleAt( pos );
  if( stc->IsString( style ) || stc->IsComment( style ) || stc->IsCharacter( style ) || stc->IsPreprocessor( style ) ) {
    return tokens;
  }
  TokenIdxSet result;
  int endOfWord = stc->WordEndPosition( pos, true );
  if( m_NativeParser.MarkItemsByAI( result, true, false, true, endOfWord ) ) {
    TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    for( TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it ) {
      const Token* token = tree->at( *it );
      if( token ) {
        tokens.push_back( CCToken( *it, token->DisplayName() ) );
        if( tokens.size() > 32 ) {
          break;
        }
      }
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  }
  return tokens;
}

wxString CodeCompletion::OnDocumentationLink( wxHtmlLinkEvent& event, bool& dismissPopup ) {
  return m_DocHelper.OnDocumentationLink( event, dismissPopup );
}

void CodeCompletion::DoAutocomplete( const CCToken& token, M_Code_Editor* ed ) {
  wxString itemText = CodeCompletionHelper::AutocompGetName( token.displayName );
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  int curPos = stc->GetCurrentPos();
  int startPos = stc->WordStartPosition( curPos, true );
  if( itemText.GetChar( 0 ) == _T( '~' ) && startPos > 0 && stc->GetCharAt( startPos - 1 ) == _T( '~' ) ) {
    --startPos;
  }
  bool needReparse = false;
  if( stc->IsPreprocessor( stc->GetStyleAt( curPos ) ) ) {
    curPos = stc->GetLineEndPosition( stc->GetCurrentLine() );
    bool addComment = ( itemText == _T( "endif" ) );
    for( int i = stc->GetCurrentPos(); i < curPos; ++i ) {
      if( stc->IsComment( stc->GetStyleAt( i ) ) ) {
        curPos = i;
        if( wxIsspace( stc->GetCharAt( i - 1 ) ) ) {
          --curPos;
        }
        addComment = false;
        break;
      }
    }
    if( addComment ) {
      wxRegEx ppIf( _T( "^[ \t]*#[ \t]*if" ) );
      wxRegEx ppEnd( _T( "^[ \t]*#[ \t]*endif" ) );
      int depth = -1;
      for( int ppLine = stc->GetCurrentLine() - 1; ppLine >= 0; --ppLine ) {
        if( stc->GetLine( ppLine ).Find( _T( '#' ) ) != wxNOT_FOUND ) {
          if( ppIf.Matches( stc->GetLine( ppLine ) ) ) {
            ++depth;
          } else if( ppEnd.Matches( stc->GetLine( ppLine ) ) ) {
            --depth;
          }
        }
        if( depth == 0 ) {
          wxRegEx pp( _T( "^[ \t]*#[ \t]*[a-z]*([ \t]+([a-zA-Z0-9_]+)|())" ) );
          pp.Matches( stc->GetLine( ppLine ) );
          if( !pp.GetMatch( stc->GetLine( ppLine ), 2 ).IsEmpty() ) {
            itemText.Append( _T( " // " ) + pp.GetMatch( stc->GetLine( ppLine ), 2 ) );
          }
          break;
        }
      }
    }
    needReparse = true;
    int pos = startPos - 1;
    wxChar ch = stc->GetCharAt( pos );
    while( ch != _T( '<' ) && ch != _T( '"' ) && ch != _T( '#' ) && ( pos > 0 ) ) {
      ch = stc->GetCharAt( --pos );
    }
    if( ch == _T( '<' ) || ch == _T( '"' ) ) {
      startPos = pos + 1;
    }
    if( ch == _T( '"' ) ) {
      itemText << _T( '"' );
    } else if( ch == _T( '<' ) ) {
      itemText << _T( '>' );
    }
  } else {
    const int endPos = stc->WordEndPosition( curPos, true );
    const wxString& alreadyText = stc->GetTextRange( curPos, endPos );
    if( !alreadyText.IsEmpty() && itemText.EndsWith( alreadyText ) ) {
      curPos = endPos;
    }
  }
  int positionModificator = 0;
  bool insideParentheses = false;
  if( token.id != -1 && m_CCAutoAddParentheses ) {
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
    const Token* tkn = tree->at( token.id );
    if( !tkn ) {
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    } else {
      bool addParentheses = tkn->m_TokenKind & tkAnyFunction;
      if( !addParentheses && ( tkn->m_TokenKind & tkMacroDef ) ) {
        if( tkn->m_Args.size() > 0 ) {
          addParentheses = true;
        }
      }
      wxString tokenArgs = tkn->GetStrippedArgs();
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
      if( addParentheses ) {
        bool insideFunction = true;
        if( m_CCDetectImplementation ) {
          ccSearchData searchData = { stc, ed->GetFilename() };
          int funcToken;
          if( m_NativeParser.FindCurrentFunctionStart( &searchData, 0, 0, &funcToken ) == -1 ) {
            itemText += tokenArgs;
            insideFunction = false;
          } else {
            CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
            const Token* parent = tree->at( funcToken );
            if( parent && ( parent->m_TokenKind & tkAnyFunction ) == 0 ) {
              itemText += tokenArgs;
              insideFunction = false;
            }
            CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
          }
        }
        if( insideFunction ) {
          if( stc->GetCharAt( curPos ) != _T( '(' ) ) {
            itemText += _T( "()" );
            if( tokenArgs.size() > 2 ) {
              positionModificator = -1;
              insideParentheses = true;
            }
          } else {
            positionModificator = 1;
          }
        }
      }
    }
  }
  stc->SetTargetStart( startPos );
  stc->SetTargetEnd( curPos );
  stc->AutoCompCancel();
  if( stc->GetTextRange( startPos, curPos ) != itemText ) {
    stc->ReplaceTarget( itemText );
  }
  stc->GotoPos( startPos + itemText.Length() + positionModificator );
  if( insideParentheses ) {
    stc->EnableTabSmartJump();
    int tooltipMode = Manager::Get()->GetConfigManager( _T( "ccmanager" ) )->ReadInt( _T( "/tooltip_mode" ), 1 );
    if( tooltipMode != 3 ) {
      CodeBlocksEvent evt( cbEVT_SHOW_CALL_TIP );
      Manager::Get()->ProcessEvent( evt );
    }
  }
  if( needReparse ) {
    m_TimerRealtimeParsing.Start( 1, wxTIMER_ONE_SHOT );
  }
  stc->ChooseCaretX();
}

wxArrayString CodeCompletion::GetLocalIncludeDirs( M_Code_Project* project, const wxArrayString& buildTargets ) {
  wxArrayString dirs;
  if( m_CCEnablePlatformCheck && false ) {
    return dirs;
  }
  const wxString prjPath = project->GetCommonTopLevelPath();
  GetAbsolutePath( prjPath, project->GetIncludeDirs(), dirs );
  for( size_t i = 0; i < buildTargets.GetCount(); ++i ) {
    ProjectBuildTarget* tgt = project->GetBuildTarget( buildTargets[i] );
    if( !m_CCEnablePlatformCheck || true ) {
      GetAbsolutePath( prjPath, tgt->GetIncludeDirs(), dirs );
    }
  }
  wxArrayString sysDirs;
  for( size_t i = 0; i < dirs.GetCount(); ) {
    if( dirs[i].StartsWith( prjPath ) ) {
      ++i;
    } else {
      wxCriticalSectionLocker locker( m_SystemHeadersThreadCS );
      if( m_SystemHeadersMap.find( dirs[i] ) == m_SystemHeadersMap.end() ) {
        sysDirs.Add( dirs[i] );
      }
      dirs.RemoveAt( i );
    }
  }
  if( !sysDirs.IsEmpty() ) {
    SystemHeadersThread* thread = new SystemHeadersThread( this, &m_SystemHeadersThreadCS, m_SystemHeadersMap, sysDirs );
    m_SystemHeadersThreads.push_back( thread );
    if( !m_SystemHeadersThreads.front()->IsRunning() && m_NativeParser.Done() ) {
      thread->Run();
    }
  }
  dirs.Sort( CodeCompletionHelper::CompareStringLen );
  return dirs;
}

wxArrayString& CodeCompletion::GetSystemIncludeDirs( M_Code_Project* project, bool force ) {
  static M_Code_Project* lastProject = nullptr;
  static wxArrayString incDirs;
  if( !force && project == lastProject ) {
    return incDirs;
  } else {
    incDirs.Clear();
    lastProject = project;
  }
  wxString prjPath;
  if( project ) {
    prjPath = project->GetCommonTopLevelPath();
  }
  ParserBase* parser = m_NativeParser.GetParserByProject( project );
  if( !parser ) {
    return incDirs;
  }
  incDirs = parser->GetIncludeDirs();
  for( size_t i = 0; i < incDirs.GetCount(); ) {
    if( incDirs[i].Last() != _T( '/' ) ) {
      incDirs[i].Append( _T( '/' ) );
    }
    if( project && incDirs[i].StartsWith( prjPath ) ) {
      incDirs.RemoveAt( i );
    } else {
      ++i;
    }
  }
  return incDirs;
}

void CodeCompletion::GetAbsolutePath( const wxString& basePath, const wxArrayString& targets, wxArrayString& dirs ) {
  for( size_t i = 0; i < targets.GetCount(); ++i ) {
    wxString includePath = targets[i];
    Manager::Get()->GetMacrosManager()->ReplaceMacros( includePath );
    wxFileName fn( includePath, wxEmptyString );
    if( fn.IsRelative() ) {
      const wxArrayString oldDirs = fn.GetDirs();
      fn.SetPath( basePath );
      for( size_t j = 0; j < oldDirs.GetCount(); ++j ) {
        fn.AppendDir( oldDirs[j] );
      }
    }
    const wxString path = fn.GetFullPath();
    if( dirs.Index( path ) == wxNOT_FOUND ) {
      dirs.Add( path );
    }
  }
}

void CodeCompletion::EditorEventHook( M_Code_Editor* editor, wxScintillaEvent& event ) {
  if( !IsAttached() || !m_InitDone ) {
    event.Skip();
    return;
  }
  if( !IsProviderFor( editor ) ) {
    event.Skip();
    return;
  }
  M_Code_StyledTextCtrl* control = editor->GetControl();
  if( m_NativeParser.GetParser().Options().whileTyping && ( ( event.GetModificationType() & wxSCI_MOD_INSERTTEXT )
      || ( event.GetModificationType() & wxSCI_MOD_DELETETEXT ) ) ) {
    m_NeedReparse = true;
  }
  if( control->GetCurrentLine() != m_CurrentLine ) {
    if( m_NeedReparse ) {
      m_TimerRealtimeParsing.Start( REALTIME_PARSING_DELAY, wxTIMER_ONE_SHOT );
      m_CurrentLength = control->GetLength();
      m_NeedReparse = false;
    }
    if( event.GetEventType() == wxEVT_SCI_UPDATEUI ) {
      m_ToolbarNeedRefresh = true;
      if( m_TimerEditorActivated.IsRunning() ) {
        m_TimerToolbar.Start( EDITOR_ACTIVATED_DELAY + 1, wxTIMER_ONE_SHOT );
      } else {
        m_TimerToolbar.Start( TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT );
      }
    }
  }
  event.Skip();
}

void CodeCompletion::RereadOptions() {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  m_LexerKeywordsToInclude[0] = cfg->ReadBool( _T( "/lexer_keywords_set1" ), true );
  m_LexerKeywordsToInclude[1] = cfg->ReadBool( _T( "/lexer_keywords_set2" ), true );
  m_LexerKeywordsToInclude[2] = cfg->ReadBool( _T( "/lexer_keywords_set3" ), false );
  m_LexerKeywordsToInclude[3] = cfg->ReadBool( _T( "/lexer_keywords_set4" ), false );
  m_LexerKeywordsToInclude[4] = cfg->ReadBool( _T( "/lexer_keywords_set5" ), false );
  m_LexerKeywordsToInclude[5] = cfg->ReadBool( _T( "/lexer_keywords_set6" ), false );
  m_LexerKeywordsToInclude[6] = cfg->ReadBool( _T( "/lexer_keywords_set7" ), false );
  m_LexerKeywordsToInclude[7] = cfg->ReadBool( _T( "/lexer_keywords_set8" ), false );
  m_LexerKeywordsToInclude[8] = cfg->ReadBool( _T( "/lexer_keywords_set9" ), false );
  m_CCMaxMatches = cfg->ReadInt( _T( "/max_matches" ), 16384 );
  m_CCAutoAddParentheses = cfg->ReadBool( _T( "/auto_add_parentheses" ), true );
  m_CCDetectImplementation = cfg->ReadBool( _T( "/detect_implementation" ), false );
  m_CCFillupChars = cfg->Read( _T( "/fillup_chars" ), wxEmptyString );
  m_CCEnableHeaders = cfg->ReadBool( _T( "/enable_headers" ), true );
  m_CCEnablePlatformCheck = cfg->ReadBool( _T( "/platform_check" ), true );
  if( m_ToolBar ) {
    UpdateToolBar();
    CodeBlocksLayoutEvent evt( cbEVT_UPDATE_VIEW_LAYOUT );
    Manager::Get()->ProcessEvent( evt );
    m_ToolbarNeedReparse = true;
    m_TimerToolbar.Start( TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT );
  }
  m_DocHelper.RereadOptions( cfg );
}

void CodeCompletion::UpdateToolBar() {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  const bool showScope = cfg->ReadBool( _T( "/scope_filter" ), true );
  const int scopeLength = cfg->ReadInt( _T( "/toolbar_scope_length" ), 280 );
  const int functionLength = cfg->ReadInt( _T( "/toolbar_function_length" ), 660 );
  if( showScope && !m_Scope ) {
    m_Scope = new wxChoice( m_ToolBar, XRCID( "chcCodeCompletionScope" ), wxPoint( 0, 0 ), wxSize( scopeLength, -1 ), 0, 0 );
    m_ToolBar->InsertControl( 0, m_Scope );
  } else if( !showScope && m_Scope ) {
    m_ToolBar->DeleteTool( m_Scope->GetId() );
    m_Scope = nullptr;
  } else if( m_Scope ) {
    m_Scope->SetSize( wxSize( scopeLength, -1 ) );
  }
  m_Function->SetSize( wxSize( functionLength, -1 ) );
  m_ToolBar->Realize();
  m_ToolBar->SetInitialSize();
}

void CodeCompletion::OnUpdateUI( wxUpdateUIEvent& event ) {
  wxString NameUnderCursor;
  bool IsInclude = false;
  const bool HasNameUnderCursor = CodeCompletionHelper::EditorHasNameUnderCursor( NameUnderCursor, IsInclude );
  const bool HasEd = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() != 0;
  if( m_EditMenu ) {
    const bool RenameEnable = HasNameUnderCursor && !IsInclude && m_NativeParser.GetParser().Done();
    m_EditMenu->Enable( idViewRenameSymbols, RenameEnable );
  }
  if( m_SearchMenu ) {
    m_SearchMenu->Enable( idMenuGotoFunction, HasEd );
    m_SearchMenu->Enable( idMenuGotoPrevFunction, HasEd );
    m_SearchMenu->Enable( idMenuGotoNextFunction, HasEd );
    const bool GotoEnable = HasNameUnderCursor && !IsInclude;
    m_SearchMenu->Enable( idMenuGotoDeclaration, GotoEnable );
    m_SearchMenu->Enable( idMenuGotoImplementation, GotoEnable );
    const bool FindEnable = HasNameUnderCursor && !IsInclude && m_NativeParser.GetParser().Done();
    m_SearchMenu->Enable( idMenuFindReferences, FindEnable );
    const bool IncludeEnable = HasNameUnderCursor && IsInclude;
    m_SearchMenu->Enable( idMenuOpenIncludeFile, IncludeEnable );
  }
  if( m_ViewMenu ) {
    bool isVis = IsWindowReallyShown( ( wxWindow* )m_NativeParser.GetClassBrowser() );
    m_ViewMenu->Check( idViewClassBrowser, isVis );
  }
  if( m_ProjectMenu ) {
    M_Code_Project* project = m_NativeParser.GetCurrentProject();
    m_ProjectMenu->Enable( idCurrentProjectReparse, project );
  }
  event.Skip();
}

void CodeCompletion::OnViewClassBrowser( wxCommandEvent& event ) {
  if( !Manager::Get()->GetConfigManager( _T( "code_completion" ) )->ReadBool( _T( "/use_symbols_browser" ), true ) ) {
    msging( _T( "The symbols browser is disabled in code-completion options.\n"
                "Please enable it there first..." ), _T( "Information" ), wxICON_INFORMATION );
    return;
  }
  CodeBlocksDockEvent evt( event.IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW );
  evt.pWindow = ( wxWindow* )m_NativeParser.GetClassBrowser();
  Manager::Get()->ProcessEvent( evt );
}

void CodeCompletion::OnGotoFunction( M_Code_unused wxCommandEvent& event ) {
  EditorManager* edMan = Manager::Get()->GetEditorManager();
  M_Code_Editor* ed = edMan->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  m_NativeParser.GetParser().ParseBufferForFunctions( ed->GetControl()->GetText() );
  TokenTree* tree = m_NativeParser.GetParser().GetTempTokenTree();
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  if( tree->empty() ) {
    msging( _T( "No functions parsed in this file..." ) );
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  } else {
    GotoFunctionDlg::Iterator iterator;
    for( size_t i = 0; i < tree->size(); i++ ) {
      Token* token = tree->at( i );
      if( token && token->m_TokenKind & tkAnyFunction ) {
        GotoFunctionDlg::FunctionToken ft;
        ft.displayName = wxString( token->DisplayName().c_str() );
        ft.name = wxString( token->m_Name.c_str() );
        ft.line = token->m_Line;
        ft.implLine = token->m_ImplLine;
        if( !token->m_FullType.empty() ) {
          ft.paramsAndreturnType = wxString( ( token->m_Args + _T( " -> " ) + token->m_FullType ).c_str() );
        } else
        { ft.paramsAndreturnType = wxString( token->m_Args.c_str() ); }
        ft.funcName = wxString( ( token->GetNamespace() + token->m_Name ).c_str() );
        iterator.AddToken( ft );
      }
    }
    tree->clear();
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    iterator.Sort();
    GotoFunctionDlg dlg( Manager::Get()->GetAppWindow(), &iterator );
    PlaceWindow( &dlg );
    if( dlg.ShowModal() == wxID_OK ) {
      int selection = dlg.GetSelection();
      if( selection != wxNOT_FOUND ) {
        const GotoFunctionDlg::FunctionToken *ft = iterator.GetToken( selection );
        if( ed && ft ) {
          ed->GotoTokenPosition( ft->implLine - 1, ft->name );
        }
      }
    }
  }
}

void CodeCompletion::OnGotoPrevFunction( M_Code_unused wxCommandEvent& event ) {
  GotoFunctionPrevNext();
}

void CodeCompletion::OnGotoNextFunction( M_Code_unused wxCommandEvent& event ) {
  GotoFunctionPrevNext( true );
}

void CodeCompletion::OnClassMethod( M_Code_unused wxCommandEvent& event ) {
  DoClassMethodDeclImpl();
}

void CodeCompletion::OnUnimplementedClassMethods( M_Code_unused wxCommandEvent& event ) {
  DoAllMethodsImpl();
}

void CodeCompletion::OnGotoDeclaration( wxCommandEvent& event ) {
  EditorManager* edMan = Manager::Get()->GetEditorManager();
  M_Code_Editor* editor = edMan->GetBuiltinActiveEditor();
  if( !editor ) {
    return;
  }
  const int pos = editor->GetControl()->GetCurrentPos();
  const int startPos = editor->GetControl()->WordStartPosition( pos, true );
  const int endPos = editor->GetControl()->WordEndPosition( pos, true );
  wxString target;
  bool isDestructor = false;
  if( CodeCompletionHelper::GetLastNonWhitespaceChar( editor->GetControl(), startPos ) == _T( '~' ) ) {
    isDestructor = true;
  }
  target << editor->GetControl()->GetTextRange( startPos, endPos );
  if( target.IsEmpty() ) {
    return;
  }
  bool isDecl = event.GetId() == idGotoDeclaration || event.GetId() == idMenuGotoDeclaration;
  bool isImpl = event.GetId() == idGotoImplementation || event.GetId() == idMenuGotoImplementation;
  TokenIdxSet result;
  m_NativeParser.MarkItemsByAI( result, true, false, true, endPos );
  TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  if( isDestructor ) {
    TokenIdxSet tmp = result;
    result.clear();
    for( TokenIdxSet::const_iterator it = tmp.begin(); it != tmp.end(); ++it ) {
      const Token* token = tree->at( *it );
      if( token && token->m_TokenKind == tkClass ) {
        token = tree->at( tree->TokenExists( _T( "~" ) + target, token->m_Index, tkDestructor ) );
        if( token ) {
          result.insert( token->m_Index );
        }
      }
    }
    if( result.empty() ) {
      result = tmp;
    }
  } else {
    const bool isFunction = CodeCompletionHelper::GetNextNonWhitespaceChar( editor->GetControl(), endPos )   == _T( '(' );
    TokenIdxSet savedResult = result;
    for( TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ) {
      const Token* token = tree->at( *it );
      if( isFunction && token && token->m_TokenKind == tkClass ) {
        result.erase( it++ );
      } else if( !isFunction && token && token->m_TokenKind == tkConstructor ) {
        result.erase( it++ );
      } else
      { ++it; }
    }
    if( !result.size() ) {
      result = savedResult;
    }
  }
  if( result.size() > 1 ) {
    const size_t curLine = editor->GetControl()->GetCurrentLine() + 1;
    for( TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it ) {
      const Token* token = tree->at( *it );
      if( token && ( token->m_Line == curLine || token->m_ImplLine == curLine ) ) {
        const int theOnlyOne = *it;
        result.clear();
        result.insert( theOnlyOne );
        break;
      }
    }
  }
  std::deque<CodeCompletionHelper::GotoDeclarationItem> foundItems;
  wxArrayString selections;
  wxString editorFilename;
  unsigned editorLine = -1;
  bool tokenFound = false;
  if( result.size() == 1 ) {
    Token* token = NULL;
    Token* sel = tree->at( *( result.begin() ) );
    if( ( isImpl && !sel->GetImplFilename().IsEmpty() ) || ( isDecl && !sel->GetFilename().IsEmpty() ) ) {
      token = sel;
    }
    if( token ) {
      if( wxGetKeyState( WXK_CONTROL ) && wxGetKeyState( WXK_SHIFT ) && ( event.GetId() == idGotoDeclaration || event.GetId() == idGotoImplementation ) ) {
        CCDebugInfo info( nullptr, &m_NativeParser.GetParser(), token );
        info.ShowModal();
      } else if( isImpl ) {
        editorFilename = token->GetImplFilename();
        editorLine = token->m_ImplLine - 1;
      } else if( isDecl ) {
        editorFilename = token->GetFilename();
        editorLine = token->m_Line - 1;
      }
      tokenFound = true;
    }
  } else if( result.size() > 1 ) {
    for( TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it ) {
      const Token* token = tree->at( *it );
      if( token ) {
        CodeCompletionHelper::GotoDeclarationItem item;
        if( isImpl ) {
          item.filename = token->GetImplFilename();
          item.line = token->m_ImplLine - 1;
        } else if( isDecl ) {
          item.filename = token->GetFilename();
          item.line = token->m_Line - 1;
        }
        if( !item.filename.empty() ) {
          selections.Add( token->DisplayName() );
          foundItems.push_back( item );
        }
      }
    }
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  if( selections.GetCount() > 1 ) {
    int sel = cbGetSingleChoiceIndex( _T( "Please make a selection:" ), _T( "Multiple matches" ), selections,
                                      Manager::Get()->GetAppWindow(), wxSize( 400, 400 ) );
    if( sel == -1 ) {
      return;
    }
    const CodeCompletionHelper::GotoDeclarationItem &item = foundItems[sel];
    editorFilename = item.filename;
    editorLine = item.line;
    tokenFound = true;
  } else if( selections.GetCount() == 1 ) {
    const CodeCompletionHelper::GotoDeclarationItem &item = foundItems.front();
    editorFilename = item.filename;
    editorLine = item.line;
    tokenFound = true;
  }
  if( tokenFound ) {
    M_Code_Editor* targetEditor = edMan->Open( editorFilename );
    if( targetEditor ) {
      targetEditor->GotoTokenPosition( editorLine, target );
    } else {
      if( isImpl ) {
        InfoWindow::Tip( F( _T( "方法未找到: %s" ), target.wx_str() ), _T( "错误" ), 3500 );
      } else if( isDecl ) {
        InfoWindow::Tip( F( _T( "声明未找到: %s" ), target.wx_str() ), _T( "错误" ), 3500 );
      }
    }
  } else {
    InfoWindow::Tip( F( _T( "没有找到: %s" ), target.wx_str() ), _T( "错误" ), 3500 );
  }
}

void CodeCompletion::OnFindReferences( M_Code_unused wxCommandEvent& event ) {
  m_CodeRefactoring.FindReferences();
}

void CodeCompletion::OnRenameSymbols( M_Code_unused wxCommandEvent& event ) {
  m_CodeRefactoring.RenameSymbols();
}

void CodeCompletion::OnOpenIncludeFile( M_Code_unused wxCommandEvent& event ) {
  wxString lastIncludeFileFrom;
  M_Code_Editor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( editor ) {
    lastIncludeFileFrom = editor->GetFilename();
  }
  bool MoveOn = false;
  wxString NameUnderCursor;
  bool IsInclude = false;
  if( CodeCompletionHelper::EditorHasNameUnderCursor( NameUnderCursor, IsInclude ) ) {
    if( IsInclude ) {
      MoveOn = true;
    }
  }
  if( !MoveOn ) {
    return;
  }
  wxArrayString foundSet = m_NativeParser.GetParser().FindFileInIncludeDirs( NameUnderCursor );
  wxFileName fname = NameUnderCursor;
  wxFileName base = lastIncludeFileFrom;
  NormalizePath( fname, base.GetPath() );
  if( wxFileExists( fname.GetFullPath() ) ) {
    foundSet.Add( fname.GetFullPath() );
  }
  M_Code_Project* project = m_NativeParser.GetProjectByEditor( editor );
  if( project ) {
    for( FilesList::const_iterator it = project->GetFilesList().begin();
         it != project->GetFilesList().end(); ++it ) {
      ProjectFile* pf = *it;
      if( !pf ) {
        continue;
      }
      if( IsSuffixOfPath( NameUnderCursor, pf->file.GetFullPath() ) ) {
        foundSet.Add( pf->file.GetFullPath() );
      }
    }
  }
  for( int i = 0; i < ( int )foundSet.Count() - 1; i++ ) {
    for( int j = i + 1; j < ( int )foundSet.Count(); ) {
      if( foundSet.Item( i ) == foundSet.Item( j ) ) {
        foundSet.RemoveAt( j );
      } else
      { j++; }
    }
  }
  wxString selectedFile;
  if( foundSet.GetCount() > 1 ) {
    SelectIncludeFile Dialog( Manager::Get()->GetAppWindow() );
    Dialog.AddListEntries( foundSet );
    PlaceWindow( &Dialog );
    if( Dialog.ShowModal() == wxID_OK ) {
      selectedFile = Dialog.GetIncludeFile();
    } else
    { return; }
  } else if( foundSet.GetCount() == 1 ) {
    selectedFile = foundSet[0];
  }
  if( !selectedFile.IsEmpty() ) {
    EditorManager* edMan = Manager::Get()->GetEditorManager();
    edMan->Open( selectedFile );
    return;
  }
  InfoWindow::Tip( F( _T( "没有找到: %s" ), NameUnderCursor.c_str() ), _T( "错误" ), 3500 );
}

void CodeCompletion::OnCurrentProjectReparse( wxCommandEvent& event ) {
  m_NativeParser.ReparseCurrentProject();
  event.Skip();
}

void CodeCompletion::OnSelectedProjectReparse( wxCommandEvent& event ) {
  m_NativeParser.ReparseSelectedProject();
  event.Skip();
}

void CodeCompletion::OnSelectedFileReparse( wxCommandEvent& event ) {
  wxTreeCtrl* tree = Manager::Get()->GetProjectManager()->GetUI().GetTree();
  if( !tree ) {
    return;
  }
  wxTreeItemId treeItem = Manager::Get()->GetProjectManager()->GetUI().GetTreeSelection();
  if( !treeItem.IsOk() ) {
    return;
  }
  const FileTreeData* data = static_cast<FileTreeData*>( tree->GetItemData( treeItem ) );
  if( !data ) {
    return;
  }
  event.Skip();
}

void CodeCompletion::OnAppDoneStartup( CodeBlocksEvent& event ) {
  if( !m_InitDone ) {
    DoParseOpenedProjectAndActiveEditor();
  }
  event.Skip();
}

void CodeCompletion::OnWorkspaceChanged( CodeBlocksEvent& event ) {
  if( IsAttached() && m_InitDone ) {
    M_Code_Project* project = Manager::Get()->GetProjectManager()->GetActiveProject();
    if( project ) {
      if( !m_NativeParser.GetParserByProject( project ) ) {
        m_NativeParser.CreateParser( project );
      }
      m_TimerToolbar.Start( TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT );
      if( m_NativeParser.GetParser().ClassBrowserOptions().displayFilter == bdfProject ) {
        m_NativeParser.UpdateClassBrowser();
      }
    }
  }
  event.Skip();
}

void CodeCompletion::OnProjectActivated( CodeBlocksEvent& event ) {
  if( !ProjectManager::IsBusy() && IsAttached() && m_InitDone ) {
    M_Code_Project* project = event.GetProject();
    if( project && !m_NativeParser.GetParserByProject( project ) && project->GetFilesCount() > 0 ) {
      m_NativeParser.CreateParser( project );
    }
    if( m_NativeParser.GetParser().ClassBrowserOptions().displayFilter == bdfProject ) {
      m_NativeParser.UpdateClassBrowser();
    }
  }
  m_NeedsBatchColour = true;
  event.Skip();
}

void CodeCompletion::OnProjectClosed( CodeBlocksEvent& event ) {
  if( IsAttached() && m_InitDone ) {
    M_Code_Project* project = event.GetProject();
    if( project && m_NativeParser.GetParserByProject( project ) ) {
      ReparsingMap::iterator it = m_ReparsingMap.find( project );
      if( it != m_ReparsingMap.end() ) {
        m_ReparsingMap.erase( it );
      }
      m_NativeParser.DeleteParser( project );
    }
  }
  event.Skip();
}

void CodeCompletion::OnProjectSaved( CodeBlocksEvent& event ) {
  m_TimerProjectSaved.SetClientData( event.GetProject() );
  m_TimerProjectSaved.Start( 200, wxTIMER_ONE_SHOT );
  event.Skip();
}

void CodeCompletion::OnProjectFileAdded( CodeBlocksEvent& event ) {
  if( IsAttached() && m_InitDone ) {
    m_NativeParser.AddFileToParser( event.GetProject(), event.GetString() );
  }
  event.Skip();
}

void CodeCompletion::OnProjectFileRemoved( CodeBlocksEvent& event ) {
  if( IsAttached() && m_InitDone ) {
    m_NativeParser.RemoveFileFromParser( event.GetProject(), event.GetString() );
  }
  event.Skip();
}

void CodeCompletion::OnProjectFileChanged( CodeBlocksEvent& event ) {
  if( IsAttached() && m_InitDone ) {
    M_Code_Project* project = event.GetProject();
    wxString filename = event.GetString();
    if( !project ) {
      project = m_NativeParser.GetProjectByFilename( filename );
    }
  }
  event.Skip();
}

void CodeCompletion::OnEditorSave( CodeBlocksEvent& event ) {
  if( !ProjectManager::IsBusy() && IsAttached() && m_InitDone && event.GetEditor() ) {
    M_Code_Project* project = event.GetProject();
    ReparsingMap::iterator it = m_ReparsingMap.find( project );
    if( it == m_ReparsingMap.end() ) {
      it = m_ReparsingMap.insert( std::make_pair( project, wxArrayString() ) ).first;
    }
    const wxString& filename = event.GetEditor()->GetFilename();
    if( it->second.Index( filename ) == wxNOT_FOUND ) {
      it->second.Add( filename );
    }
    m_TimerReparsing.Start( EDITOR_ACTIVATED_DELAY + it->second.GetCount() * 10, wxTIMER_ONE_SHOT );
  }
  event.Skip();
}

void CodeCompletion::OnEditorOpen( CodeBlocksEvent& event ) {
  if( !Manager::IsAppShuttingDown() && IsAttached() && m_InitDone ) {
    M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor( event.GetEditor() );
    if( ed ) {
      FunctionsScopePerFile* funcdata = &( m_AllFunctionsScopes[ed->GetFilename()] );
      funcdata->parsed = false;
    }
  }
  event.Skip();
}

void CodeCompletion::OnEditorActivated( CodeBlocksEvent& event ) {
  if( !ProjectManager::IsBusy() && IsAttached() && m_InitDone && event.GetEditor() ) {
    m_LastEditor = Manager::Get()->GetEditorManager()->GetBuiltinEditor( event.GetEditor() );
    m_TimerEditorActivated.Start( EDITOR_ACTIVATED_DELAY, wxTIMER_ONE_SHOT );
    if( m_TimerToolbar.IsRunning() ) {
      m_TimerToolbar.Stop();
    }
  }
  event.Skip();
}

void CodeCompletion::OnEditorClosed( CodeBlocksEvent& event ) {
  EditorManager* edm = Manager::Get()->GetEditorManager();
  if( !edm ) {
    event.Skip();
    return;
  }
  wxString activeFile;
  EditorBase* eb = edm->GetActiveEditor();
  if( eb ) {
    activeFile = eb->GetFilename();
  }
  if( m_LastEditor == event.GetEditor() ) {
    m_LastEditor = nullptr;
    if( m_TimerEditorActivated.IsRunning() ) {
      m_TimerEditorActivated.Stop();
    }
  }
  if( edm->GetBuiltinEditor( event.GetEditor() ) ) {
    m_NativeParser.OnEditorClosed( event.GetEditor() );
  }
  m_LastFile.Clear();
  if( edm->GetEditorsCount() == 0 || !edm->GetActiveEditor() || !edm->GetActiveEditor()->IsBuiltinEditor() ) {
    EnableToolbarTools( false );
    if( m_Scope ) {
      m_Scope->Clear();
    }
    if( m_Function ) {
      m_Function->Clear();
    }
    M_Code_Editor* ed = edm->GetBuiltinEditor( event.GetEditor() );
    wxString filename;
    if( ed ) {
      filename = ed->GetFilename();
    }
    m_AllFunctionsScopes[filename].m_FunctionsScope.clear();
    m_AllFunctionsScopes[filename].m_NameSpaces.clear();
    m_AllFunctionsScopes[filename].parsed = false;
    if( m_NativeParser.GetParser().ClassBrowserOptions().displayFilter == bdfFile ) {
      m_NativeParser.UpdateClassBrowser();
    }
  }
  event.Skip();
}

void CodeCompletion::OnCCLogger( CodeBlocksThreadEvent& event ) {
  if( !Manager::IsAppShuttingDown() ) {
    Manager::Get()->GetLogManager()->Log( event.GetString() );
  }
}

void CodeCompletion::OnCCDebugLogger( CodeBlocksThreadEvent& event ) {
  if( !Manager::IsAppShuttingDown() ) {
    Manager::Get()->GetLogManager()->DebugLog( event.GetString() );
  }
}

void CodeCompletion::OnParserStart( wxCommandEvent& event ) {
  M_Code_Project* project = static_cast<M_Code_Project*>( event.GetClientData() );
  ParserCommon::ParserState state = static_cast<ParserCommon::ParserState>( event.GetInt() );
  if( state == ParserCommon::ptCreateParser ) {
    if( m_CCEnableHeaders ) {
      wxArrayString& dirs = GetSystemIncludeDirs( project, true );
      SystemHeadersThread* thread = new SystemHeadersThread( this, &m_SystemHeadersThreadCS, m_SystemHeadersMap, dirs );
      m_SystemHeadersThreads.push_back( thread );
    }
    M_Code_Editor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if( m_NativeParser.GetProjectByEditor( editor ) == project ) {
      EnableToolbarTools( false );
    }
  }
}

void CodeCompletion::OnParserEnd( wxCommandEvent& event ) {
  ParserCommon::ParserState state = static_cast<ParserCommon::ParserState>( event.GetInt() );
  if( state == ParserCommon::ptCreateParser ) {
    if( m_CCEnableHeaders && !m_SystemHeadersThreads.empty() && !m_SystemHeadersThreads.front()->IsRunning()
        && m_NativeParser.Done() ) {
      m_SystemHeadersThreads.front()->Run();
    }
  }
  EditorManager* edMan = Manager::Get()->GetEditorManager();
  M_Code_Editor* editor = edMan->GetBuiltinActiveEditor();
  if( editor ) {
    m_ToolbarNeedReparse = true;
    m_TimerToolbar.Start( TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT );
  }
  if( m_NeedsBatchColour ) {
    for( int edIdx = edMan->GetEditorsCount() - 1; edIdx >= 0; --edIdx ) {
      editor = edMan->GetBuiltinEditor( edIdx );
      if( editor ) {
        UpdateEditorSyntax( editor );
      }
    }
    m_NeedsBatchColour = false;
  }
  event.Skip();
}

void CodeCompletion::OnSystemHeadersThreadUpdate( CodeBlocksThreadEvent& event ) {
  if( !m_SystemHeadersThreads.empty() ) {
    SystemHeadersThread* thread = static_cast<SystemHeadersThread*>( event.GetClientData() );
    if( thread == m_SystemHeadersThreads.front() ) {
      CCLogger::Get()->DebugLog( event.GetString() );
    }
  }
}

void CodeCompletion::OnSystemHeadersThreadFinish( CodeBlocksThreadEvent& event ) {
  if( m_SystemHeadersThreads.empty() ) {
    return;
  }
  SystemHeadersThread* thread = static_cast<SystemHeadersThread*>( event.GetClientData() );
  if( thread == m_SystemHeadersThreads.front() ) {
    if( !event.GetString().IsEmpty() ) {
      CCLogger::Get()->DebugLog( event.GetString() );
    }
    if( thread->IsAlive() && thread->IsRunning() ) {
      thread->Wait();
    }
    m_SystemHeadersThreads.pop_front();
  }
  if( m_CCEnableHeaders && !m_SystemHeadersThreads.empty()
      && !m_SystemHeadersThreads.front()->IsRunning()
      && m_NativeParser.Done() ) {
    m_SystemHeadersThreads.front()->Run();
  }
}

void CodeCompletion::OnSystemHeadersThreadError( CodeBlocksThreadEvent& event ) {
  if( !m_SystemHeadersThreads.empty() ) {
    SystemHeadersThread* thread = static_cast<SystemHeadersThread*>( event.GetClientData() );
    if( thread == m_SystemHeadersThreads.front() ) {
      CCLogger::Get()->DebugLog( event.GetString() );
    }
  }
}

int CodeCompletion::DoClassMethodDeclImpl() {
  if( !IsAttached() || !m_InitDone ) {
    return -1;
  }
  EditorManager* edMan = Manager::Get()->GetEditorManager();
  M_Code_Editor* ed = edMan->GetBuiltinActiveEditor();
  if( !ed ) {
    return -3;
  }
  FileType ft = FileTypeOf( ed->GetShortName() );
  if( ft != ftHeader && ft != ftSource && ft != ftTemplateSource ) {
    return -4;
  }
  if( !m_NativeParser.GetParser().Done() ) {
    wxString msg = _T( "The Parser is still parsing files." );
    msg += m_NativeParser.GetParser().NotDoneReason();
    CCLogger::Get()->DebugLog( msg );
    return -5;
  }
  int success = -6;
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  wxString filename = ed->GetFilename();
  InsertClassMethodDlg dlg( Manager::Get()->GetAppWindow(), &m_NativeParser.GetParser(), filename );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    M_Code_StyledTextCtrl* control = ed->GetControl();
    int pos = control->GetCurrentPos();
    int line = control->LineFromPosition( pos );
    control->GotoPos( control->PositionFromLine( line ) );
    wxArrayString result = dlg.GetCode();
    for( unsigned int i = 0; i < result.GetCount(); ++i ) {
      pos = control->GetCurrentPos();
      line = control->LineFromPosition( pos );
      wxString str = ed->GetLineIndentString( line - 1 ) + result[i];
      MatchCodeStyle( str, control->GetEOLMode(), ed->GetLineIndentString( line - 1 ), control->GetUseTabs(), control->GetTabWidth() );
      control->SetTargetStart( pos );
      control->SetTargetEnd( pos );
      control->ReplaceTarget( str );
      control->GotoPos( pos + str.Length() );
    }
    success = 0;
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return success;
}

int CodeCompletion::DoAllMethodsImpl() {
  if( !IsAttached() || !m_InitDone ) {
    return -1;
  }
  EditorManager* edMan = Manager::Get()->GetEditorManager();
  M_Code_Editor* ed = edMan->GetBuiltinActiveEditor();
  if( !ed ) {
    return -3;
  }
  FileType ft = FileTypeOf( ed->GetShortName() );
  if( ft != ftHeader && ft != ftSource && ft != ftTemplateSource ) {
    return -4;
  }
  wxArrayString paths = m_NativeParser.GetAllPathsByFilename( ed->GetFilename() );
  TokenTree*    tree  = m_NativeParser.GetParser().GetTokenTree();
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  TokenFileSet result;
  for( size_t i = 0; i < paths.GetCount(); ++i ) {
    TokenFileSet result_file;
    tree->GetFileMatches( paths[i], result_file, true, true );
    for( TokenFileSet::const_iterator it = result_file.begin(); it != result_file.end(); ++it ) {
      result.insert( *it );
    }
  }
  if( result.empty() ) {
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    msging( _T( "Could not find any file match in parser's database." ), _T( "警告" ), wxICON_WARNING );
    return -5;
  }
  wxArrayString arr;
  wxArrayInt arrint;
  typedef std::map<int, std::pair<int, wxString> > ImplMap;
  ImplMap im;
  for( TokenFileSet::const_iterator itf = result.begin(); itf != result.end(); ++itf ) {
    const TokenIdxSet* tokens = tree->GetTokensBelongToFile( *itf );
    if( !tokens ) {
      continue;
    }
    for( TokenIdxSet::const_iterator its = tokens->begin(); its != tokens->end(); ++its ) {
      const Token* token = tree->at( *its );
      if( token && ( token->m_TokenKind & ( tkFunction | tkConstructor | tkDestructor ) )
          && token->m_ImplLine == 0 ) {
        im[token->m_Line] = std::make_pair( *its, token->DisplayName() );
      }
    }
  }
  for( ImplMap::const_iterator it = im.begin(); it != im.end(); ++it ) {
    arrint.Add( it->second.first );
    arr.Add( it->second.second );
  }
  if( arr.empty() ) {
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    msging( _T( "No classes declared or no un-implemented class methods found." ), _T( "警告" ), wxICON_WARNING );
    return -5;
  }
  int success = -5;
  MultiSelectDlg dlg( Manager::Get()->GetAppWindow(), arr, true );
  if( dlg.ShowModal() == wxID_OK ) {
    M_Code_StyledTextCtrl* control = ed->GetControl();
    int pos = control->GetCurrentPos();
    int line = control->LineFromPosition( pos );
    control->GotoPos( control->PositionFromLine( line ) );
    bool addDoxgenComment = Manager::Get()->GetConfigManager( _T( "code_completion" ) )->ReadBool( _T( "/add_doxgen_comment" ), false );
    wxArrayInt indices = dlg.GetSelectedIndices();
    for( size_t i = 0; i < indices.GetCount(); ++i ) {
      const Token* token = tree->at( arrint[indices[i]] );
      if( !token ) {
        continue;
      }
      pos  = control->GetCurrentPos();
      line = control->LineFromPosition( pos );
      wxString str;
      if( i > 0 ) {
        str << _T( "\n" );
      } else
      { str << ed->GetLineIndentString( line - 1 ); }
      if( addDoxgenComment ) {
        str << _T( "\n" );
      }
      wxString type = token->m_FullType;
      if( !type.IsEmpty() ) {
        if( ( type.Last() == _T( '&' ) || type.Last() == _T( '*' ) )
            && type[type.Len() - 2] == _T( ' ' ) ) {
          type[type.Len() - 2] = type.Last();
          type.RemoveLast();
        }
        str << type << _T( " " );
      }
      if( token->m_ParentIndex != -1 ) {
        const Token* parent = tree->at( token->m_ParentIndex );
        if( parent ) {
          str << parent->m_Name << _T( "::" );
        }
      }
      str << token->m_Name << token->GetStrippedArgs();
      if( token->m_IsConst ) {
        str << _T( " const" );
      }
      if( token->m_IsNoExcept ) {
        str << _T( " noexcept" );
      }
      str << _T( "\n{\n\t\n}\n" );
      MatchCodeStyle( str, control->GetEOLMode(), ed->GetLineIndentString( line - 1 ), control->GetUseTabs(), control->GetTabWidth() );
      control->SetTargetStart( pos );
      control->SetTargetEnd( pos );
      control->ReplaceTarget( str );
      control->GotoPos( pos + str.Length() );
    }
    if( !indices.IsEmpty() ) {
      pos  = control->GetCurrentPos();
      line = control->LineFromPosition( pos );
      control->GotoPos( control->GetLineEndPosition( line - 2 ) );
    }
    success = 0;
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return success;
}

void CodeCompletion::MatchCodeStyle( wxString& str, int eolStyle, const wxString& indent, bool useTabs, int tabSize ) {
  str.Replace( _T( "\n" ), _T( "\n" ) + indent );
  if( !useTabs ) {
    str.Replace( _T( "\t" ), wxString( _T( ' ' ), tabSize ) );
  }
  if( !indent.IsEmpty() ) {
    str.RemoveLast( indent.Length() );
  }
}

void CodeCompletion::FunctionPosition( int &scopeItem, int &functionItem ) const {
  scopeItem = -1;
  functionItem = -1;
  for( unsigned int idxSc = 0; idxSc < m_ScopeMarks.size(); ++idxSc ) {
    unsigned int start = m_ScopeMarks[idxSc];
    unsigned int end = ( idxSc + 1 < m_ScopeMarks.size() ) ? m_ScopeMarks[idxSc + 1] : m_FunctionsScope.size();
    for( int idxFn = 0; start + idxFn < end; ++idxFn ) {
      const FunctionScope fs = m_FunctionsScope[start + idxFn];
      if( m_CurrentLine >= fs.StartLine && m_CurrentLine <= fs.EndLine ) {
        scopeItem = idxSc;
        functionItem = idxFn;
      }
    }
  }
}

void CodeCompletion::GotoFunctionPrevNext( bool next ) {
  EditorManager* edMan = Manager::Get()->GetEditorManager();
  M_Code_Editor* ed = edMan->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  int current_line = ed->GetControl()->GetCurrentLine();
  if( !m_FunctionsScope.size() ) {
    return;
  }
  int line = -1;
  unsigned int best_func = 0;
  bool found_best_func = false;
  for( unsigned int idx_func = 0; idx_func < m_FunctionsScope.size(); ++idx_func ) {
    int best_func_line  = m_FunctionsScope[best_func].StartLine;
    int func_start_line = m_FunctionsScope[idx_func].StartLine;
    if( next ) {
      if( best_func_line  > current_line ) {
        if( ( func_start_line > current_line ) && ( func_start_line < best_func_line ) ) {
          best_func = idx_func;
          found_best_func = true;
        }
      } else if( func_start_line > current_line ) {
        best_func = idx_func;
        found_best_func = true;
      }
    } else {
      if( best_func_line  < current_line ) {
        if( ( func_start_line < current_line )
            && ( func_start_line > best_func_line ) ) {
          best_func = idx_func;
          found_best_func = true;
        }
      } else if( func_start_line < current_line ) {
        best_func = idx_func;
        found_best_func = true;
      }
    }
  }
  if( found_best_func ) {
    line = m_FunctionsScope[best_func].StartLine;
  } else if( next && m_FunctionsScope[best_func].StartLine > current_line ) {
    line = m_FunctionsScope[best_func].StartLine;
  } else if( !next && m_FunctionsScope[best_func].StartLine < current_line ) {
    line = m_FunctionsScope[best_func].StartLine;
  }
  if( line != -1 ) {
    ed->GotoLine( line );
    ed->SetFocus();
  }
}

int CodeCompletion::NameSpacePosition() const {
  int pos = -1;
  int startLine = -1;
  for( unsigned int idxNs = 0; idxNs < m_NameSpaces.size(); ++idxNs ) {
    const NameSpace& ns = m_NameSpaces[idxNs];
    if( m_CurrentLine >= ns.StartLine && m_CurrentLine <= ns.EndLine && ns.StartLine > startLine ) {
      pos = static_cast<int>( idxNs );
      startLine = ns.StartLine;
    }
  }
  return pos;
}

void CodeCompletion::OnScope( wxCommandEvent& ) {
  int sel = m_Scope->GetSelection();
  if( sel != -1 && sel < static_cast<int>( m_ScopeMarks.size() ) ) {
    UpdateFunctions( sel );
  }
}

void CodeCompletion::OnFunction( M_Code_unused wxCommandEvent& event ) {
  int selSc = ( m_Scope ) ? m_Scope->GetSelection() : 0;
  if( selSc != -1 && selSc < static_cast<int>( m_ScopeMarks.size() ) ) {
    int idxFn = m_ScopeMarks[selSc] + m_Function->GetSelection();
    if( idxFn != -1 && idxFn < static_cast<int>( m_FunctionsScope.size() ) ) {
      M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
      if( ed ) {
        ed->GotoTokenPosition( m_FunctionsScope[idxFn].StartLine, m_FunctionsScope[idxFn].ShortName );
      }
    }
  }
}


void CodeCompletion::ParseFunctionsAndFillToolbar() {
  EditorManager* edMan = Manager::Get()->GetEditorManager();
  if( !edMan ) {
    return;
  }
  M_Code_Editor* ed = edMan->GetBuiltinActiveEditor();
  if( !ed || !ed->GetControl() ) {
    if( m_Function ) {
      m_Function->Clear();
    }
    if( m_Scope ) {
      m_Scope->Clear();
    }
    EnableToolbarTools( false );
    m_LastFile.Clear();
    return;
  }
  const wxString filename = ed->GetFilename();
  if( filename.IsEmpty() ) {
    return;
  }
  bool fileParseFinished = m_NativeParser.GetParser().IsFileParsed( filename );
  FunctionsScopePerFile* funcdata = &( m_AllFunctionsScopes[filename] );
  if( m_ToolbarNeedReparse || !funcdata->parsed ) {
    if( m_ToolbarNeedReparse ) {
      m_ToolbarNeedReparse = false;
    }
    funcdata->m_FunctionsScope.clear();
    funcdata->m_NameSpaces.clear();
    TokenIdxSet result;
    m_NativeParser.GetParser().FindTokensInFile( filename, result, tkAnyFunction | tkEnum | tkClass | tkNamespace );
    if( !result.empty() ) {
      funcdata->parsed = true;
    } else {
      fileParseFinished = false;
    }
    TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    for( TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it ) {
      const Token* token = tree->at( *it );
      if( token && token->m_ImplLine != 0 ) {
        FunctionScope fs;
        fs.StartLine = token->m_ImplLine    - 1;
        fs.EndLine = token->m_ImplLineEnd - 1;
        const size_t fileIdx = tree->InsertFileOrGetIndex( filename );
        if( token->m_TokenKind & tkAnyFunction && fileIdx == token->m_ImplFileIdx ) {
          fs.Scope = token->GetNamespace();
          if( fs.Scope.IsEmpty() ) {
            fs.Scope = g_GlobalScope;
          }
          wxString result_str = token->m_Name;
          fs.ShortName = result_str;
          result_str << token->GetFormattedArgs();
          if( !token->m_BaseType.IsEmpty() ) {
            result_str << _T( " : " ) << token->m_BaseType;
          }
          fs.Name = result_str;
          funcdata->m_FunctionsScope.push_back( fs );
        } else if( token->m_TokenKind & ( tkEnum | tkClass | tkNamespace ) ) {
          fs.Scope = token->GetNamespace() + token->m_Name + _T( "::" );
          funcdata->m_FunctionsScope.push_back( fs );
        }
      }
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    FunctionsScopeVec& functionsScopes = funcdata->m_FunctionsScope;
    NameSpaceVec& nameSpaces = funcdata->m_NameSpaces;
    m_NativeParser.GetParser().ParseBufferForNamespaces( ed->GetControl()->GetText(), nameSpaces );
    std::sort( nameSpaces.begin(), nameSpaces.end(), CodeCompletionHelper::LessNameSpace );
    std::copy( nameSpaces.begin(), nameSpaces.end(), back_inserter( functionsScopes ) );
    std::sort( functionsScopes.begin(), functionsScopes.end(), CodeCompletionHelper::LessFunctionScope );
    FunctionsScopeVec::const_iterator it;
    it = unique( functionsScopes.begin(), functionsScopes.end(), CodeCompletionHelper::EqualFunctionScope );
    functionsScopes.resize( it - functionsScopes.begin() );
    if( !m_ToolbarNeedRefresh ) {
      m_ToolbarNeedRefresh = true;
    }
  }
  m_FunctionsScope = funcdata->m_FunctionsScope;
  m_NameSpaces = funcdata->m_NameSpaces;
  m_ScopeMarks.clear();
  unsigned int fsSize = m_FunctionsScope.size();
  if( !m_FunctionsScope.empty() ) {
    m_ScopeMarks.push_back( 0 );
    if( m_Scope ) {
      wxString lastScope = m_FunctionsScope[0].Scope;
      for( unsigned int idx = 1; idx < fsSize; ++idx ) {
        const wxString& currentScope = m_FunctionsScope[idx].Scope;
        if( lastScope != currentScope ) {
          m_ScopeMarks.push_back( idx );
          lastScope = currentScope;
        }
      }
    }
  }
  #if CC_CODECOMPLETION_DEBUG_OUTPUT == 1
  for( unsigned int i = 0; i < m_FunctionsScope.size(); ++i )
    CCLogger::Get()->DebugLog( F( _T( "\t%s%s (%d:%d)" ),
                                  m_FunctionsScope[i].Scope.wx_str(), m_FunctionsScope[i].Name.wx_str(),
                                  m_FunctionsScope[i].StartLine, m_FunctionsScope[i].EndLine ) );
  #endif
  if( m_ToolbarNeedRefresh || m_LastFile != filename ) {
    if( m_ToolbarNeedRefresh ) {
      m_ToolbarNeedRefresh = false;
    }
    if( m_LastFile != filename ) {
      m_LastFile = filename;
    }
    m_Function->Clear();
    if( m_Scope ) {
      m_Scope->Freeze();
      m_Scope->Clear();
      for( unsigned int idxSc = 0; idxSc < m_ScopeMarks.size(); ++idxSc ) {
        int idxFn = m_ScopeMarks[idxSc];
        const FunctionScope& fs = m_FunctionsScope[idxFn];
        m_Scope->Append( fs.Scope );
      }
      m_Scope->Thaw();
    } else {
      m_Function->Freeze();
      for( unsigned int idxFn = 0; idxFn < m_FunctionsScope.size(); ++idxFn ) {
        const FunctionScope& fs = m_FunctionsScope[idxFn];
        if( fs.Name != wxEmptyString ) {
          m_Function->Append( fs.Scope + fs.Name );
        } else if( fs.Scope.EndsWith( _T( "::" ) ) ) {
          m_Function->Append( fs.Scope.substr( 0, fs.Scope.length() - 2 ) );
        } else {
          m_Function->Append( fs.Scope );
        }
      }
      m_Function->Thaw();
    }
  }
  FindFunctionAndUpdate( ed->GetControl()->GetCurrentLine() );
  EnableToolbarTools( fileParseFinished );
}

void CodeCompletion::FindFunctionAndUpdate( int currentLine ) {
  if( currentLine == -1 ) {
    return;
  }
  m_CurrentLine = currentLine;
  int selSc, selFn;
  FunctionPosition( selSc, selFn );
  if( m_Scope ) {
    if( selSc != -1 && selSc != m_Scope->GetSelection() ) {
      m_Scope->SetSelection( selSc );
      UpdateFunctions( selSc );
    } else if( selSc == -1 ) {
      m_Scope->SetSelection( -1 );
    }
  }
  if( selFn != -1 && selFn != m_Function->GetSelection() ) {
    m_Function->SetSelection( selFn );
  } else if( selFn == -1 ) {
    m_Function->SetSelection( -1 );
    wxChoice* choice = ( m_Scope ) ? m_Scope : m_Function;
    int NsSel = NameSpacePosition();
    if( NsSel != -1 ) {
      choice->SetStringSelection( m_NameSpaces[NsSel].Name );
    } else if( !m_Scope ) {
      choice->SetSelection( -1 );
    } else {
      choice->SetStringSelection( g_GlobalScope );
      wxCommandEvent evt( wxEVT_COMMAND_CHOICE_SELECTED, XRCID( "chcCodeCompletionScope" ) );
      wxPostEvent( this, evt );
    }
  }
}

void CodeCompletion::UpdateFunctions( unsigned int scopeItem ) {
  m_Function->Freeze();
  m_Function->Clear();
  unsigned int idxEnd = ( scopeItem + 1 < m_ScopeMarks.size() ) ? m_ScopeMarks[scopeItem + 1] : m_FunctionsScope.size();
  for( unsigned int idxFn = m_ScopeMarks[scopeItem]; idxFn < idxEnd; ++idxFn ) {
    const wxString &name = m_FunctionsScope[idxFn].Name;
    m_Function->Append( name );
  }
  m_Function->Thaw();
}

void CodeCompletion::EnableToolbarTools( bool enable ) {
  if( m_Scope ) {
    m_Scope->Enable( enable );
  }
  if( m_Function ) {
    m_Function->Enable( enable );
  }
}

void CodeCompletion::DoParseOpenedProjectAndActiveEditor() {
  m_InitDone = true;
  M_Code_Project* curProject = Manager::Get()->GetProjectManager()->GetActiveProject();
  if( curProject && !m_NativeParser.GetParserByProject( curProject ) ) {
    m_NativeParser.CreateParser( curProject );
  }
  EditorBase* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( editor ) {
    m_NativeParser.OnEditorActivated( editor );
  }
}

void CodeCompletion::UpdateEditorSyntax( M_Code_Editor* ed ) {
  if( !Manager::Get()->GetConfigManager( _T( "code_completion" ) )->ReadBool( _T( "/semantic_keywords" ), false ) ) {
    return;
  }
  if( !ed ) {
    ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  }
  if( !ed || ed->GetControl()->GetLexer() != wxSCI_LEX_CPP ) {
    return;
  }
  TokenIdxSet result;
  int flags = tkAnyContainer | tkAnyFunction;
  if( ed->GetFilename().EndsWith( _T( ".c" ) ) ) {
    flags |= tkVariable;
  }
  m_NativeParser.GetParser().FindTokensInFile( ed->GetFilename(), result, flags );
  TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
  std::set<wxString> varList;
  TokenIdxSet parsedTokens;
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  for( TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it ) {
    Token* token = tree->at( *it );
    if( !token ) {
      continue;
    }
    if( token->m_TokenKind == tkVariable ) {
      varList.insert( token->m_Name );
      continue;
    } else if( token->m_TokenKind & tkAnyFunction ) {
      if( token->m_ParentIndex == wxNOT_FOUND ) {
        continue;
      } else
      { token = tree->at( token->m_ParentIndex ); }
    }
    if( !token || parsedTokens.find( token->m_Index ) != parsedTokens.end() ) {
      continue;
    }
    parsedTokens.insert( token->m_Index );
    for( TokenIdxSet::const_iterator chIt = token->m_Children.begin();
         chIt != token->m_Children.end(); ++chIt ) {
      const Token* chToken = tree->at( *chIt );
      if( chToken && chToken->m_TokenKind == tkVariable ) {
        varList.insert( chToken->m_Name );
      }
    }
    if( token->m_Ancestors.empty() ) {
      tree->RecalcInheritanceChain( token );
    }
    for( TokenIdxSet::const_iterator ancIt = token->m_Ancestors.begin();
         ancIt != token->m_Ancestors.end(); ++ancIt ) {
      const Token* ancToken = tree->at( *ancIt );
      if( !ancToken || parsedTokens.find( ancToken->m_Index ) != parsedTokens.end() ) {
        continue;
      }
      for( TokenIdxSet::const_iterator chIt = ancToken->m_Children.begin();
           chIt != ancToken->m_Children.end(); ++chIt ) {
        const Token* chToken = tree->at( *chIt );
        if( chToken && chToken->m_TokenKind == tkVariable && chToken->m_Scope != tsPrivate ) {
          varList.insert( chToken->m_Name );
        }
      }
    }
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  wxString keywords = colour_set->GetKeywords( ed->GetLanguage(), 3 );
  for( std::set<wxString>::const_iterator keyIt = varList.begin();
       keyIt != varList.end(); ++keyIt ) {
    keywords += _T( " " ) + *keyIt;
  }
  ed->GetControl()->SetKeyWords( 3, keywords );
  ed->GetControl()->Colourise( 0, -1 );
}

void CodeCompletion::OnToolbarTimer( M_Code_unused wxTimerEvent& event ) {
  if( !ProjectManager::IsBusy() ) {
    ParseFunctionsAndFillToolbar();
  } else {
    m_TimerToolbar.Start( TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT );
  }
}

void CodeCompletion::OnRealtimeParsingTimer( M_Code_unused wxTimerEvent& event ) {
  M_Code_Editor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !editor ) {
    return;
  }
  const int curLen = editor->GetControl()->GetLength();
  if( curLen != m_CurrentLength ) {
    m_CurrentLength = curLen;
    m_TimerRealtimeParsing.Start( REALTIME_PARSING_DELAY, wxTIMER_ONE_SHOT );
    return;
  }
  M_Code_Project* project = m_NativeParser.GetProjectByEditor( editor );
  if( project && !project->GetFileByFilename( m_LastFile, false, true ) ) {
    return;
  }
}

void CodeCompletion::OnProjectSavedTimer( M_Code_unused wxTimerEvent& event ) {
  M_Code_Project* project = static_cast<M_Code_Project*>( m_TimerProjectSaved.GetClientData() );
  m_TimerProjectSaved.SetClientData( NULL );
  ProjectsArray* projs = Manager::Get()->GetProjectManager()->GetProjects();
  if( projs->Index( project ) == wxNOT_FOUND ) {
    return;
  }
  if( IsAttached() && m_InitDone && project ) {
    if( project &&  m_NativeParser.GetParserByProject( project ) ) {
      ReparsingMap::iterator it = m_ReparsingMap.find( project );
      if( it != m_ReparsingMap.end() ) {
        m_ReparsingMap.erase( it );
      }
      if( m_NativeParser.DeleteParser( project ) ) {
        m_NativeParser.CreateParser( project );
      }
    }
  }
}

void CodeCompletion::OnReparsingTimer( M_Code_unused wxTimerEvent& event ) {
  if( ProjectManager::IsBusy() || !IsAttached() || !m_InitDone ) {
    m_ReparsingMap.clear();
    return;
  }
  ReparsingMap::iterator it = m_ReparsingMap.begin();
  if( it != m_ReparsingMap.end() && m_NativeParser.Done() ) {
    M_Code_Project* project = it->first;
    wxArrayString& files = it->second;
    if( !project ) {
      project = m_NativeParser.GetProjectByFilename( files[0] );
    }
    if( project && Manager::Get()->GetProjectManager()->IsProjectStillOpen( project ) ) {
      wxString curFile;
      EditorBase* editor = Manager::Get()->GetEditorManager()->GetActiveEditor();
      if( editor ) {
        curFile = editor->GetFilename();
      }
      size_t reparseCount = 0;
      while( !files.IsEmpty() ) {
        if( m_NativeParser.ReparseFile( project, files.Last() ) ) {
          ++reparseCount;
          if( files.Last() == curFile ) {
            m_ToolbarNeedReparse = true;
            m_TimerToolbar.Start( TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT );
          }
        }
        files.RemoveAt( files.GetCount() - 1 );
      }
    }
    if( files.IsEmpty() ) {
      m_ReparsingMap.erase( it );
    }
  }
  if( !m_ReparsingMap.empty() ) {
    m_TimerReparsing.Start( EDITOR_ACTIVATED_DELAY, wxTIMER_ONE_SHOT );
  }
}

void CodeCompletion::OnEditorActivatedTimer( M_Code_unused wxTimerEvent& event ) {
  EditorBase* editor  = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !editor || editor != m_LastEditor ) {
    EnableToolbarTools( false );
    return;
  }
  const wxString& curFile = editor->GetFilename();
  if( !m_LastFile.IsEmpty() && m_LastFile == curFile ) {
    return;
  }
  m_NativeParser.OnEditorActivated( editor );
  m_TimerToolbar.Start( TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT );
  UpdateEditorSyntax();
}
